/*
 * Tencent is pleased to support the open source community by making Angel available.
 *
 * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/Apache-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 */


package com.tencent.angel.ml.math2.ufuncs.executor.simple;

import com.tencent.angel.exception.AngelException;
import com.tencent.angel.ml.math2.storage.*;
import com.tencent.angel.ml.math2.ufuncs.executor.StorageSwitch;
import com.tencent.angel.ml.math2.ufuncs.expression.Binary;
import com.tencent.angel.ml.math2.vector.*;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import com.tencent.angel.ml.math2.utils.Constant;

public class SimpleBinaryInNonZAExecutor {
    public static Vector apply(Vector v1, Vector v2, Binary op) {
        if (v1 instanceof IntDoubleVector && v2 instanceof IntDoubleVector) {
            return apply((IntDoubleVector) v1, (IntDoubleVector) v2, op);
        } else if (v1 instanceof IntDoubleVector && v2 instanceof IntFloatVector) {
            return apply((IntDoubleVector) v1, (IntFloatVector) v2, op);
        } else if (v1 instanceof IntDoubleVector && v2 instanceof IntLongVector) {
            return apply((IntDoubleVector) v1, (IntLongVector) v2, op);
        } else if (v1 instanceof IntDoubleVector && v2 instanceof IntIntVector) {
            return apply((IntDoubleVector) v1, (IntIntVector) v2, op);
        } else if (v1 instanceof IntDoubleVector && v2 instanceof IntDummyVector) {
            return apply((IntDoubleVector) v1, (IntDummyVector) v2, op);
        } else if (v1 instanceof IntFloatVector && v2 instanceof IntFloatVector) {
            return apply((IntFloatVector) v1, (IntFloatVector) v2, op);
        } else if (v1 instanceof IntFloatVector && v2 instanceof IntLongVector) {
            return apply((IntFloatVector) v1, (IntLongVector) v2, op);
        } else if (v1 instanceof IntFloatVector && v2 instanceof IntIntVector) {
            return apply((IntFloatVector) v1, (IntIntVector) v2, op);
        } else if (v1 instanceof IntFloatVector && v2 instanceof IntDummyVector) {
            return apply((IntFloatVector) v1, (IntDummyVector) v2, op);
        } else if (v1 instanceof IntLongVector && v2 instanceof IntLongVector) {
            return apply((IntLongVector) v1, (IntLongVector) v2, op);
        } else if (v1 instanceof IntLongVector && v2 instanceof IntIntVector) {
            return apply((IntLongVector) v1, (IntIntVector) v2, op);
        } else if (v1 instanceof IntLongVector && v2 instanceof IntDummyVector) {
            return apply((IntLongVector) v1, (IntDummyVector) v2, op);
        } else if (v1 instanceof IntIntVector && v2 instanceof IntIntVector) {
            return apply((IntIntVector) v1, (IntIntVector) v2, op);
        } else if (v1 instanceof IntIntVector && v2 instanceof IntDummyVector) {
            return apply((IntIntVector) v1, (IntDummyVector) v2, op);
        } else if (v1 instanceof LongDoubleVector && v2 instanceof LongDoubleVector) {
            return apply((LongDoubleVector) v1, (LongDoubleVector) v2, op);
        } else if (v1 instanceof LongDoubleVector && v2 instanceof LongFloatVector) {
            return apply((LongDoubleVector) v1, (LongFloatVector) v2, op);
        } else if (v1 instanceof LongDoubleVector && v2 instanceof LongLongVector) {
            return apply((LongDoubleVector) v1, (LongLongVector) v2, op);
        } else if (v1 instanceof LongDoubleVector && v2 instanceof LongIntVector) {
            return apply((LongDoubleVector) v1, (LongIntVector) v2, op);
        } else if (v1 instanceof LongDoubleVector && v2 instanceof LongDummyVector) {
            return apply((LongDoubleVector) v1, (LongDummyVector) v2, op);
        } else if (v1 instanceof LongFloatVector && v2 instanceof LongFloatVector) {
            return apply((LongFloatVector) v1, (LongFloatVector) v2, op);
        } else if (v1 instanceof LongFloatVector && v2 instanceof LongLongVector) {
            return apply((LongFloatVector) v1, (LongLongVector) v2, op);
        } else if (v1 instanceof LongFloatVector && v2 instanceof LongIntVector) {
            return apply((LongFloatVector) v1, (LongIntVector) v2, op);
        } else if (v1 instanceof LongFloatVector && v2 instanceof LongDummyVector) {
            return apply((LongFloatVector) v1, (LongDummyVector) v2, op);
        } else if (v1 instanceof LongLongVector && v2 instanceof LongLongVector) {
            return apply((LongLongVector) v1, (LongLongVector) v2, op);
        } else if (v1 instanceof LongLongVector && v2 instanceof LongIntVector) {
            return apply((LongLongVector) v1, (LongIntVector) v2, op);
        } else if (v1 instanceof LongLongVector && v2 instanceof LongDummyVector) {
            return apply((LongLongVector) v1, (LongDummyVector) v2, op);
        } else if (v1 instanceof LongIntVector && v2 instanceof LongIntVector) {
            return apply((LongIntVector) v1, (LongIntVector) v2, op);
        } else if (v1 instanceof LongIntVector && v2 instanceof LongDummyVector) {
            return apply((LongIntVector) v1, (LongDummyVector) v2, op);
        } else {
            throw new AngelException("Vector type is not support!");
        }
    }

    public static Vector apply(IntDoubleVector v1, IntDoubleVector v2, Binary op) {
    	IntDoubleVectorStorage newStorage = (IntDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            double [ ] v1Values = newStorage.getValues();
            double [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            double [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2DoubleMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getDoubleValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            double [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            double [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              double [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2DoubleMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getDoubleValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                double [ ] resValues = newStorage.getValues();
                double [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2DoubleMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getDoubleValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                double [] resValues = newStorage.getValues();
                double [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                double [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                double [ ] resValues = newStorage.getValues();
                double [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    double [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntDoubleVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2DoubleMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Int2DoubleMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2DoubleMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2DoubleMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Int2DoubleMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2DoubleMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  double [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  double [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				double [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				double [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2DoubleMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getDoubleValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2DoubleMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getDoubleValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        double [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntDoubleVector v1, IntFloatVector v2, Binary op) {
    	IntDoubleVectorStorage newStorage = (IntDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            double [ ] v1Values = newStorage.getValues();
            float [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            double [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2FloatMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getFloatValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            double [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            float [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              float [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2DoubleMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getDoubleValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                double [ ] resValues = newStorage.getValues();
                float [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2DoubleMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getDoubleValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                double [] resValues = newStorage.getValues();
                float [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                double [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                double [ ] resValues = newStorage.getValues();
                float [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    double [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntDoubleVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2FloatMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Int2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2FloatMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Int2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2FloatMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        float [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntDoubleVector v1, IntLongVector v2, Binary op) {
    	IntDoubleVectorStorage newStorage = (IntDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            double [ ] v1Values = newStorage.getValues();
            long [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            double [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2LongMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getLongValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            double [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            long [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              long [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2DoubleMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getDoubleValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                double [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2DoubleMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getDoubleValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                double [] resValues = newStorage.getValues();
                long [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                double [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                double [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    double [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntDoubleVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2LongMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2LongMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2LongMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntDoubleVector v1, IntIntVector v2, Binary op) {
    	IntDoubleVectorStorage newStorage = (IntDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            double [ ] v1Values = newStorage.getValues();
            int [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            double [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2IntMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getIntValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            double [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            int [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              int [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2DoubleMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getDoubleValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                double [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2DoubleMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getDoubleValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                double [] resValues = newStorage.getValues();
                int [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                double [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                double [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    double [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntDoubleVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2IntMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2IntMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2IntMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2DoubleMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2DoubleMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntFloatVector v1, IntFloatVector v2, Binary op) {
    	IntFloatVectorStorage newStorage = (IntFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            float [ ] v1Values = newStorage.getValues();
            float [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            float [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2FloatMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getFloatValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            float [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            float [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              float [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2FloatMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getFloatValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                float [ ] resValues = newStorage.getValues();
                float [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2FloatMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getFloatValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                float [] resValues = newStorage.getValues();
                float [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                float [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                float [ ] resValues = newStorage.getValues();
                float [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    float [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntFloatVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2FloatMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Int2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2FloatMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Int2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2FloatMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2FloatMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        float [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntFloatVector v1, IntLongVector v2, Binary op) {
    	IntFloatVectorStorage newStorage = (IntFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            float [ ] v1Values = newStorage.getValues();
            long [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            float [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2LongMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getLongValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            float [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            long [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              long [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2FloatMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getFloatValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                float [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2FloatMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getFloatValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                float [] resValues = newStorage.getValues();
                long [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                float [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                float [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    float [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntFloatVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2LongMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2LongMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2LongMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntFloatVector v1, IntIntVector v2, Binary op) {
    	IntFloatVectorStorage newStorage = (IntFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            float [ ] v1Values = newStorage.getValues();
            int [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            float [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2IntMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getIntValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            float [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            int [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              int [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2FloatMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getFloatValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                float [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2FloatMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getFloatValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                float [] resValues = newStorage.getValues();
                int [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                float [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                float [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    float [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntFloatVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2IntMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2IntMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2IntMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2FloatMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2FloatMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntLongVector v1, IntLongVector v2, Binary op) {
    	IntLongVectorStorage newStorage = (IntLongVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            long [ ] v1Values = newStorage.getValues();
            long [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            long [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2LongMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getLongValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            long [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            long [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              long [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2LongMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2LongMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getLongValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                long [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2LongMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2LongMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getLongValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                long [] resValues = newStorage.getValues();
                long [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                long [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                long [ ] resValues = newStorage.getValues();
                long [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    long [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntLongVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2LongMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2LongMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2LongMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2LongMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				ObjectIterator<Int2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2LongMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2LongMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2LongMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2LongMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        long [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          long [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				long [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntLongVector v1, IntIntVector v2, Binary op) {
    	IntLongVectorStorage newStorage = (IntLongVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            long [ ] v1Values = newStorage.getValues();
            int [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            long [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2IntMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getIntValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            long [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            int [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              int [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2LongMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2LongMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getLongValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                long [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2LongMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2LongMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getLongValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                long [] resValues = newStorage.getValues();
                int [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                long [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                long [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    long [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntLongVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2IntMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2LongMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2IntMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2LongMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2IntMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2LongMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2LongMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        long [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          long [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				long [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntIntVector v1, IntIntVector v2, Binary op) {
    	IntIntVectorStorage newStorage = (IntIntVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isDense() && v2.isDense()) {
            int [ ] v1Values = newStorage.getValues();
            int [ ] v2Values = v2.getStorage().getValues();
            for (int idx = 0; idx < v1Values.length; idx++) {
                v1Values[idx] = op.apply(v1Values[idx], v2Values[idx]);
            }
        } else if (v1.isDense() && v2.isSparse()) {
            int [ ] v1Values = newStorage.getValues();
            ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
            while(iter.hasNext()) {
                Int2IntMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                v1Values[idx] = op.apply(v1Values[idx], entry.getIntValue());
            }
        } else if (v1.isDense() && v2.isSorted()) {
            int [ ] v1Values = newStorage.getValues();
            int [ ] v2Indices = v2.getStorage().getIndices();
            int [ ] v2Values = v2.getStorage().getValues();
            int size = v2.size();
            for (int i=0; i < size; i++) {
                int idx = v2Indices[i];
                v1Values[idx] = op.apply(v1Values[idx], v2Values[i]);
            }
        } else if (v1.isSparse() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
              int [] v2Values = v2.getStorage().getValues();

              if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                for (int i = 0; i < dim; i++) {
                newStorage.set(i, op.apply(0, v2Values[i]));
                }
                ObjectIterator<Int2IntMap.Entry> iter = v1.getStorage().entryIterator();
                while (iter.hasNext()) {
                Int2IntMap.Entry entry = iter.next();
                int idx = entry.getIntKey();
                newStorage.set(idx, op.apply(entry.getIntValue(), v2Values[idx]));
                }
              } else {
                for (int i = 0; i < dim; i++) {
                if (v1.getStorage().hasKey(i)) {
                  newStorage.set(i, op.apply(v1.get(i), v2Values[i]));
                } else {
                  newStorage.set(i, op.apply(0, v2Values[i]));
                }
                }
              }
            } else {
                int [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }
                    ObjectIterator<Int2IntMap.Entry> iter = v1.getStorage().entryIterator();
                    while (iter.hasNext()) {
                        Int2IntMap.Entry entry = iter.next();
                        int idx = entry.getIntKey();
                        resValues[idx] = op.apply(entry.getIntValue(), v2Values[idx]);
                    }
                } else {
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1.getStorage().hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSorted() && v2.isDense()) {
            if (op.isKeepStorage()) {
                int dim = v1.getDim();
                int [] resIndices = newStorage.getIndices();
                int [] resValues = newStorage.getValues();
                int [] v2Values = v2.getStorage().getValues();

                int [] v1Indices = v1.getStorage().getIndices();
                int [] v1Values = v1.getStorage().getValues();
                for (int i = 0; i < dim; i++) {
                  resIndices[i] = i;
                  resValues[i] = op.apply(0, v2Values[i]);
                }

                int size = v1.size();
                for (int i = 0; i < size; i++) {
                  int idx = v1Indices[i];
                  resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                }
            } else {
                int [ ] resValues = newStorage.getValues();
                int [ ] v2Values = v2.getStorage().getValues();

                if (v1.size() < Constant.denseLoopThreshold * v1.getDim()) {
                    int [ ] v1Indices = v1.getStorage().getIndices();
                    int [ ] v1Values = v1.getStorage().getValues();
                    for (int i = 0; i < resValues.length; i++) {
                        resValues[i] = op.apply(0, v2Values[i]);
                    }

                    int size = v1.size();
                    for (int i = 0; i < size; i++) {
                        int idx = v1Indices[i];
                        resValues[idx] = op.apply(v1Values[i], v2Values[idx]);
                    }
                } else {
                    IntIntVectorStorage v1Storage = v1.getStorage();
                    for (int i = 0; i < resValues.length; i++) {
                        if (v1Storage.hasKey(i)) {
                            resValues[i] = op.apply(v1.get(i), v2Values[i]);
                        } else {
                            resValues[i] = op.apply(0, v2Values[i]);
                        }
                    }
                }
            }
        } else if (v1.isSparse() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Int2IntMap.Entry entry = iter.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Int2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2IntMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getIntValue());
			  }

			  ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Int2IntMap.Entry entry = iter2.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Int2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2IntMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getIntValue());
				}

				ObjectIterator<Int2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Int2IntMap.Entry entry = iter2.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Int2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Int2IntMap.Entry entry = iter1.next();
				int idx = entry.getIntKey();
				newStorage.set(idx, entry.getIntValue());
			  }

			  int [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  int size = v2.size();
			  for (int i = 0; i < size; i++) {
				int idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Int2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Int2IntMap.Entry entry = iter1.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, entry.getIntValue());
				}

				int [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				int size = v2.size();
				for (int i = 0; i < size; i++) {
				  int idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			int v1Size = v1.size();
			int v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				int [] values = new int [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntIntSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				int [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				int [] v1Indices = v1.getStorage().getIndices();
				int [] idxiter = v2.getStorage().indexIterator().toIntArray();

				int [] indices = new int [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				IntAVLTreeSet avl = new IntAVLTreeSet(indices);

				IntBidirectionalIterator iter = avl.iterator();
				int [] values = new int [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  int idx = iter.nextInt();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new IntIntSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				int [] v1Indices = v1.getStorage().getIndices();
				int [] v1Values = v1.getStorage().getValues();
				int size = v1.size();
				for (int i = 0; i < size; i++) {
				  int idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Int2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Int2IntMap.Entry entry = iter.next();
				  int idx = entry.getIntKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        int size1 = v1.size();
        int size2 = v2.size();

        int [ ] v1Indices = v1.getStorage().getIndices();
        int [ ] v1Values = v1.getStorage().getValues();
        int [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          int [] resIndices = newStorage.getIndices();
          int [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				int [] resIndices = newStorage.getIndices();
				int [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongDoubleVector v1, LongDoubleVector v2, Binary op) {
    	LongDoubleVectorStorage newStorage = (LongDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2DoubleMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Long2DoubleMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2DoubleMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2DoubleMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Long2DoubleMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2DoubleMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getDoubleValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  double [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  double [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				double [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				double [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2DoubleMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getDoubleValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2DoubleMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2DoubleMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getDoubleValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        double [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongDoubleVector v1, LongFloatVector v2, Binary op) {
    	LongDoubleVectorStorage newStorage = (LongDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2FloatMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Long2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2FloatMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Long2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2FloatMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        float [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongDoubleVector v1, LongLongVector v2, Binary op) {
    	LongDoubleVectorStorage newStorage = (LongDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2LongMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2LongMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2LongMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongDoubleVector v1, LongIntVector v2, Binary op) {
    	LongDoubleVectorStorage newStorage = (LongDoubleVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2IntMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2IntMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2IntMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2DoubleMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getDoubleValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2DoubleMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getDoubleValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				double [] values = new double [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongDoubleSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				double [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        double [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          double [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				double [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongFloatVector v1, LongFloatVector v2, Binary op) {
    	LongFloatVectorStorage newStorage = (LongFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2FloatMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Long2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2FloatMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Long2FloatMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2FloatMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getFloatValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  float [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				float [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2FloatMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2FloatMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getFloatValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        float [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongFloatVector v1, LongLongVector v2, Binary op) {
    	LongFloatVectorStorage newStorage = (LongFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2LongMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2LongMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2LongMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongFloatVector v1, LongIntVector v2, Binary op) {
    	LongFloatVectorStorage newStorage = (LongFloatVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2IntMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2IntMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2IntMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2FloatMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getFloatValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2FloatMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getFloatValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				float [] values = new float [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongFloatSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				float [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        float [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          float [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				float [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongLongVector v1, LongLongVector v2, Binary op) {
    	LongLongVectorStorage newStorage = (LongLongVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2LongMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2LongMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2LongMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2LongMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				ObjectIterator<Long2LongMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2LongMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getLongValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2LongMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  long [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2LongMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				long [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2LongMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2LongMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getLongValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        long [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        long [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          long [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				long [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongLongVector v1, LongIntVector v2, Binary op) {
    	LongLongVectorStorage newStorage = (LongLongVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2IntMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2LongMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2IntMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2LongMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2IntMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2LongMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getLongValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2LongMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getLongValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				long [] values = new long [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongLongSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				long [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        long [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          long [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				long [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongIntVector v1, LongIntVector v2, Binary op) {
    	LongIntVectorStorage newStorage = (LongIntVectorStorage)StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
				  (v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
			  while (iter.hasNext()) {
				Long2IntMap.Entry entry = iter.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss dense storage is more efficient
			  ObjectIterator<Long2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2IntMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getIntValue());
			  }

			  ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
			  while (iter2.hasNext()) {
				Long2IntMap.Entry entry = iter2.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				// no rehashor one onle rehash is required, nothing to optimization
				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  } else {
				// multi-rehash
				ObjectIterator<Long2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2IntMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getIntValue());
				}

				ObjectIterator<Long2IntMap.Entry> iter2 = v2.getStorage().entryIterator();
				while (iter2.hasNext()) {
				  Long2IntMap.Entry entry = iter2.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(v1.get(idx), entry.getIntValue()));
				}
			  }
			}
        } else if (v1.isSparse() && v2.isSorted()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if (v1Size >= v2Size * Constant.sparseThreshold &&
					(v1Size + v2Size) * Constant.intersectionCoeff <= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  // we gauss the indices of v2 maybe is a subset of v1, or overlap is very large
			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  for (int i = 0; i < v2.size(); i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			}else if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sparseDenseStorageThreshold * v1.dim()) {
			  ObjectIterator<Long2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
			  while (iter1.hasNext()) {
				Long2IntMap.Entry entry = iter1.next();
				long idx = entry.getLongKey();
				newStorage.set(idx, entry.getIntValue());
			  }

			  long [] v2Indices = v2.getStorage().getIndices();
			  int [] v2Values = v2.getStorage().getValues();
			  long size = v2.size();
			  for (int i = 0; i < size; i++) {
				long idx = v2Indices[i];
				newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
			  }
			} else {
			  // to avoid multi-rehash
			  int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
			  if (v1.size() + v2.size() <= 1.5 * capacity) {
				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				for (int i = 0; i < v2.size(); i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  } else {
				ObjectIterator<Long2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
				while (iter1.hasNext()) {
				  Long2IntMap.Entry entry = iter1.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, entry.getIntValue());
				}

				long [] v2Indices = v2.getStorage().getIndices();
				int [] v2Values = v2.getStorage().getValues();
				long size = v2.size();
				for (int i = 0; i < size; i++) {
				  long idx = v2Indices[i];
				  newStorage.set(idx, op.apply(v1.get(idx), v2Values[i]));
				}
			  }
			}

      } else if (v1.isSorted() && v2.isSparse()) {
			long v1Size = v1.size();
			long v2Size = v2.size();

			if ((v1Size + v2Size) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()){
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				int [] values = new int [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongIntSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				int [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				   newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}else {
			  if (op.isKeepStorage()){
				long [] v1Indices = v1.getStorage().getIndices();
				long [] idxiter = v2.getStorage().indexIterator().toLongArray();

				long [] indices = new long [(int)(v1Size + v2Size)];
				System.arraycopy(v1Indices, 0, indices, 0, (int) v1.size());
				System.arraycopy(idxiter, 0, indices, (int) v1.size(), (int) v2.size());

				LongAVLTreeSet avl = new LongAVLTreeSet(indices);

				LongBidirectionalIterator iter = avl.iterator();
				int [] values = new int [indices.length];

				int i = 0;
				while (iter.hasNext()) {
				  long idx = iter.nextLong();
				  indices[i] = idx;
				  values[i] = op.apply(v1.get(idx), v2.get(idx));
				  i++;
				}

				while (i < indices.length) {
				  indices[i] = 0;
				  i++;
				}

				newStorage = new LongIntSortedVectorStorage(v1.getDim(), (int) avl.size(), indices, values);
			  }else{
				long [] v1Indices = v1.getStorage().getIndices();
				int [] v1Values = v1.getStorage().getValues();
				long size = v1.size();
				for (int i = 0; i < size; i++) {
				  long idx = v1Indices[i];
				  newStorage.set(idx, v1Values[i]);
				}

				ObjectIterator<Long2IntMap.Entry> iter = v2.getStorage().entryIterator();
				while (iter.hasNext()) {
				  Long2IntMap.Entry entry = iter.next();
				  long idx = entry.getLongKey();
				  newStorage.set(idx, op.apply(newStorage.get(idx), entry.getIntValue()));
				}
			  }
			}
      } else if (v1.isSorted() && v2.isSorted()) {
            int v1Pointor = 0;
        int v2Pointor = 0;
        long size1 = v1.size();
        long size2 = v2.size();

        long [ ] v1Indices = v1.getStorage().getIndices();
        int [ ] v1Values = v1.getStorage().getValues();
        long [ ] v2Indices = v2.getStorage().getIndices();
        int [ ] v2Values = v2.getStorage().getValues();
        if ((size1 + size2) * Constant.intersectionCoeff >= Constant.sortedDenseStorageThreshold * v1.dim()) {
          if (op.isKeepStorage()){//sorted
          long [] resIndices = newStorage.getIndices();
          int [] resValues = newStorage.getValues();
          int global = 0;

          while (v1Pointor < size1 && v2Pointor < size2) {
            if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
            global++;
            v1Pointor++;
            v2Pointor++;
            } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
            resIndices[global] = v1Indices[v1Pointor];
            resValues[global] = v1Values[v1Pointor];
            global++;
            v1Pointor++;
            } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
            resIndices[global] = v2Indices[v2Pointor];
            resValues[global] = op.apply(0, v2Values[v2Pointor]);
            global++;
            v2Pointor++;
            }
          }
			  } else {//dense
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			} else {
			  if (op.isKeepStorage()){
				long [] resIndices = newStorage.getIndices();
				int [] resValues = newStorage.getValues();
				int globalPointor = 0;

				while (v1Pointor < size1 && v2Pointor < size2) {
				  if (v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = op.apply(v1Values[v1Pointor], v2Values[v2Pointor]);
					v1Pointor++;
					v2Pointor++;
					globalPointor++;
				  } else if (v1Indices[v1Pointor] < v2Indices[v2Pointor]) {
					resIndices[globalPointor] = v1Indices[v1Pointor];
					resValues[globalPointor] = v1Values[v1Pointor];
					v1Pointor++;
					globalPointor++;
				  } else { // v1Indices[v1Pointor] > v2Indices[v2Pointor]
					resIndices[globalPointor] = v2Indices[v2Pointor];
					resValues[globalPointor] = op.apply(0, v2Values[v2Pointor]);
					v2Pointor++;
					globalPointor++;
				  }
				}
			  }else {
          while (v1Pointor < size1 || v2Pointor < size2) {
            if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
              newStorage
                  .set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], v2Values[v2Pointor]));
              v1Pointor++;
              v2Pointor++;
            } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                (v1Pointor < size1 && v2Pointor >= size2)) {
              newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
              v1Pointor++;
            } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                (v1Pointor >= size1 && v2Pointor < size2)){
              newStorage.set(v2Indices[v2Pointor], op.apply(0, v2Values[v2Pointor]));
              v2Pointor++;
            }
          }
			  }
			}
        } else {
            throw new AngelException("The operation is not support!");
        }
 		v1.setStorage(newStorage);

        return v1;
    }



    public static Vector apply(IntDoubleVector v1, IntDummyVector v2, Binary op) {
    	IntDoubleVectorStorage newStorage = (IntDoubleVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isDense()) {
            double [] v1Values = newStorage.getValues();
            int [] v2Indices = v2.getIndices();
            for(int idx: v2Indices) {
                v1Values[idx] = op.apply(v1Values[idx], 1);
            }
        } else if (v1.isSparse()) {
            int [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                double [] resValues = newStorage.getValues();

                ObjectIterator<Int2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Int2DoubleMap.Entry entry = iter.next();
                    newStorage.set(entry.getIntKey(), entry.getDoubleValue());
                }

                for(int idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                int size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Int2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Int2DoubleMap.Entry entry = iter1.next();
                        int idx = entry.getIntKey();
                        newStorage.set(idx, entry.getDoubleValue());
                    }

                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            int [] v1Indices = v1.getStorage().getIndices();
            int [] v2Indices = v2.getIndices();

 			int size1 = v1.size();
            int size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                double [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    int idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              double [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntFloatVector v1, IntDummyVector v2, Binary op) {
    	IntFloatVectorStorage newStorage = (IntFloatVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isDense()) {
            float [] v1Values = newStorage.getValues();
            int [] v2Indices = v2.getIndices();
            for(int idx: v2Indices) {
                v1Values[idx] = op.apply(v1Values[idx], 1);
            }
        } else if (v1.isSparse()) {
            int [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                float [] resValues = newStorage.getValues();

                ObjectIterator<Int2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Int2FloatMap.Entry entry = iter.next();
                    newStorage.set(entry.getIntKey(), entry.getFloatValue());
                }

                for(int idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                int size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Int2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Int2FloatMap.Entry entry = iter1.next();
                        int idx = entry.getIntKey();
                        newStorage.set(idx, entry.getFloatValue());
                    }

                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            int [] v1Indices = v1.getStorage().getIndices();
            int [] v2Indices = v2.getIndices();

 			int size1 = v1.size();
            int size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                float [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    int idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              float [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntLongVector v1, IntDummyVector v2, Binary op) {
    	IntLongVectorStorage newStorage = (IntLongVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isDense()) {
            long [] v1Values = newStorage.getValues();
            int [] v2Indices = v2.getIndices();
            for(int idx: v2Indices) {
                v1Values[idx] = op.apply(v1Values[idx], 1);
            }
        } else if (v1.isSparse()) {
            int [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                long [] resValues = newStorage.getValues();

                ObjectIterator<Int2LongMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Int2LongMap.Entry entry = iter.next();
                    newStorage.set(entry.getIntKey(), entry.getLongValue());
                }

                for(int idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                int size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Int2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Int2LongMap.Entry entry = iter1.next();
                        int idx = entry.getIntKey();
                        newStorage.set(idx, entry.getLongValue());
                    }

                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            int [] v1Indices = v1.getStorage().getIndices();
            int [] v2Indices = v2.getIndices();

 			int size1 = v1.size();
            int size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                long [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    int idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              long [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(IntIntVector v1, IntDummyVector v2, Binary op) {
    	IntIntVectorStorage newStorage = (IntIntVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isDense()) {
            int [] v1Values = newStorage.getValues();
            int [] v2Indices = v2.getIndices();
            for(int idx: v2Indices) {
                v1Values[idx] = op.apply(v1Values[idx], 1);
            }
        } else if (v1.isSparse()) {
            int [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                int [] resValues = newStorage.getValues();

                ObjectIterator<Int2IntMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Int2IntMap.Entry entry = iter.next();
                    newStorage.set(entry.getIntKey(), entry.getIntValue());
                }

                for(int idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                int size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Int2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Int2IntMap.Entry entry = iter1.next();
                        int idx = entry.getIntKey();
                        newStorage.set(idx, entry.getIntValue());
                    }

                    for (int i = 0; i < size; i++) {
                        int idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            int [] v1Indices = v1.getStorage().getIndices();
            int [] v2Indices = v2.getIndices();

 			int size1 = v1.size();
            int size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                int [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    int idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              int [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongDoubleVector v1, LongDummyVector v2, Binary op) {
    	LongDoubleVectorStorage newStorage = (LongDoubleVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse()) {
            long [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                double [] resValues = newStorage.getValues();

                ObjectIterator<Long2DoubleMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Long2DoubleMap.Entry entry = iter.next();
                    newStorage.set(entry.getLongKey(), entry.getDoubleValue());
                }

                for(long idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                long size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Long2DoubleMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Long2DoubleMap.Entry entry = iter1.next();
                        long idx = entry.getLongKey();
                        newStorage.set(idx, entry.getDoubleValue());
                    }

                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            long [] v1Indices = v1.getStorage().getIndices();
            long [] v2Indices = v2.getIndices();

 			long size1 = v1.size();
            long size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                double [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    long idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              double [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongFloatVector v1, LongDummyVector v2, Binary op) {
    	LongFloatVectorStorage newStorage = (LongFloatVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse()) {
            long [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                float [] resValues = newStorage.getValues();

                ObjectIterator<Long2FloatMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Long2FloatMap.Entry entry = iter.next();
                    newStorage.set(entry.getLongKey(), entry.getFloatValue());
                }

                for(long idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                long size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Long2FloatMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Long2FloatMap.Entry entry = iter1.next();
                        long idx = entry.getLongKey();
                        newStorage.set(idx, entry.getFloatValue());
                    }

                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            long [] v1Indices = v1.getStorage().getIndices();
            long [] v2Indices = v2.getIndices();

 			long size1 = v1.size();
            long size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                float [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    long idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              float [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongLongVector v1, LongDummyVector v2, Binary op) {
    	LongLongVectorStorage newStorage = (LongLongVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse()) {
            long [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                long [] resValues = newStorage.getValues();

                ObjectIterator<Long2LongMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Long2LongMap.Entry entry = iter.next();
                    newStorage.set(entry.getLongKey(), entry.getLongValue());
                }

                for(long idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                long size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Long2LongMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Long2LongMap.Entry entry = iter1.next();
                        long idx = entry.getLongKey();
                        newStorage.set(idx, entry.getLongValue());
                    }

                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            long [] v1Indices = v1.getStorage().getIndices();
            long [] v2Indices = v2.getIndices();

 			long size1 = v1.size();
            long size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                long [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    long idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              long [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

    public static Vector apply(LongIntVector v1, LongDummyVector v2, Binary op) {
    	LongIntVectorStorage newStorage = (LongIntVectorStorage) StorageSwitch.apply(v1, v2, op);
        if (v1.isSparse()) {
            long [] v2Indices = v2.getIndices();

            if (((v1.size() + v2.size())* Constant.intersectionCoeff
            	> Constant.sparseDenseStorageThreshold * v1.getDim())) {
                int [] resValues = newStorage.getValues();

                ObjectIterator<Long2IntMap.Entry> iter = v1.getStorage().entryIterator();
                while(iter.hasNext()){
                    Long2IntMap.Entry entry = iter.next();
                    newStorage.set(entry.getLongKey(), entry.getIntValue());
                }

                for(long idx: v2Indices) {
                    newStorage.set(idx, op.apply(v1.get(idx), 1));
                }

            } else {
                // to avoid multi-rehash
                int capacity = 1 << (32 - Integer.numberOfLeadingZeros((int) (v1.size() / 0.75)));
                long size = v2.size();
                if (v1.size() + v2.size() < 1.5 * capacity) {
                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                } else {
                    ObjectIterator<Long2IntMap.Entry> iter1 = v1.getStorage().entryIterator();
                    while(iter1.hasNext()) {
                        Long2IntMap.Entry entry = iter1.next();
                        long idx = entry.getLongKey();
                        newStorage.set(idx, entry.getIntValue());
                    }

                    for (int i = 0; i < size; i++) {
                        long idx = v2Indices[i];
                        newStorage.set(idx, op.apply(v1.get(idx),1));
                    }
                }
            }
        } else { // sorted
            long [] v1Indices = v1.getStorage().getIndices();
            long [] v2Indices = v2.getIndices();

 			long size1 = v1.size();
            long size2 = v2.size();
            if (!op.isKeepStorage() && ((size1 + size2)* Constant.intersectionCoeff
            	> Constant.sortedDenseStorageThreshold * v1.getDim())) {
                int [] v1Values = v1.getStorage().getValues();

                for (int i=0; i < size1; i++){
                    newStorage.set(v1Indices[i], v1Values[i]);
                }

                for (int i = 0; i < size2; i++) {
                    long idx = v2Indices[i];
                    newStorage.set(idx, op.apply(newStorage.get(idx), 1));
                }

            } else {
            	int v1Pointor = 0;
              int v2Pointor = 0;

              int [] v1Values = v1.getStorage().getValues();

              while (v1Pointor < size1 || v2Pointor < size2) {
                if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] == v2Indices[v2Pointor]) {
                  newStorage.set(v1Indices[v1Pointor], op.apply(v1Values[v1Pointor], 1));
                  v1Pointor++;
                  v2Pointor++;
                } else if ((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] < v2Indices[v2Pointor] ||
                    (v1Pointor < size1 && v2Pointor >= size2)) {
                  newStorage.set(v1Indices[v1Pointor], v1Values[v1Pointor]);
                  v1Pointor++;
                } else if (((v1Pointor < size1 && v2Pointor < size2) && v1Indices[v1Pointor] >= v2Indices[v2Pointor]) ||
                    (v1Pointor >= size1 && v2Pointor < size2)){
                  newStorage.set(v2Indices[v2Pointor], op.apply(0, 1));
                  v2Pointor++;
                }
              }
            }
        }
        v1.setStorage(newStorage);

        return v1;
    }

}